using System;
using System.Collections.Generic;
using UnityEngine;
using WooEngine.Common;
using WooEngine.Utility.Logger;

namespace WooEngine.NetWork
{
    public interface INetWorkProvider
    {
        string Tag { get; }

        bool IsCanOP { get; }

        long UID { get; }

        byte[] Token { get; }

        void SetUserInfo(long uid, byte[] token);

        void Connect(Action<string> callBack = null);

        void Send(string smname, byte[] data, bool isWaitReceive);

        void Recv();

        void Release();
    }

    public class NetWorkManager : SingletonBehaviour<NetWorkManager>
    {
        public const string PingName = "beatheart";

        private ITimerTask m_pingTask;

#if ODIN_INSPECTOR
        [ShowInInspector, ShowIf("showOdinInfo"), DictionaryDrawerSettings(IsReadOnly = true, DisplayMode = DictionaryDisplayOptions.OneLine)]
#endif
        private readonly Dictionary<string, INetWorkProvider> m_netProviders = new Dictionary<string, INetWorkProvider>();
        private readonly List<INetWorkProvider> m_SafeAdded = new List<INetWorkProvider>();
        private readonly List<string> m_SafeRemoved = new List<string>();

        public T CreateProvider<T>(string tag, string url, string port = null) where T : INetWorkProvider
        {
            if (!m_netProviders.TryGetValue(tag, out INetWorkProvider provider))
            {
                provider = (INetWorkProvider)System.Activator.CreateInstance(typeof(T), tag, url, port);
                m_SafeAdded.Add(provider);
            }
            return (T)provider;
        }

        

        public void RemoveProvider(string tag)
        {
            if (m_netProviders.TryGetValue(tag, out INetWorkProvider provider))
            {
                m_SafeRemoved.Add(tag);
            }
        }

        public bool TryGetProvider<T>(string tag, out T provider) where T : INetWorkProvider
        {
            if (m_netProviders.TryGetValue(tag, out INetWorkProvider result))
            {
                provider = (T)result;
                return true;
            }
            provider = default;
            return false;
        }

        public void Connect(Action<string> callBack)
        {
            if (!m_netProviders.TryGetValue(tag, out INetWorkProvider provider))
            {
                provider.Connect(callBack);
            }
        }

        public bool IsCanOP(string tag)
        {
            if (m_netProviders.TryGetValue(tag, out INetWorkProvider result))
            {
                return result.IsCanOP;
            }
            return false;
        }

        public void SetUserInfo(long uid, byte[] token)
        {
            if (m_netProviders.TryGetValue(tag, out INetWorkProvider result))
            {
                result.SetUserInfo(uid, token);
            }
        }

        public void Send(string tag, string smname, byte[] data, bool isWaitReceive)
        {
            if (m_netProviders.TryGetValue(tag, out INetWorkProvider result))
            {
                result.Send(smname, data, isWaitReceive);
            }
        }

        protected override void OnUpdate(float delta)
        {
            base.OnUpdate(delta);

            if (m_SafeRemoved.Count > 0)
            {
                for (int i = 0; i < m_SafeRemoved.Count; i++)
                {
                    string tag = m_SafeRemoved[i];
                    if (m_netProviders.TryGetValue(tag, out INetWorkProvider provider))
                    {
                        provider.Release();
                        m_netProviders.Remove(tag);
                    }
                    m_SafeRemoved.Clear();
                }
            }

            if(m_SafeAdded.Count > 0)
            {
                for (int i = 0; i < m_SafeAdded.Count; i++)
                {
                    INetWorkProvider provider = m_SafeAdded[i];
                    if (!m_netProviders.ContainsKey(provider.Tag))
                    {
                        m_netProviders.Add(provider.Tag, provider);
                    }
                    m_SafeAdded.Clear();
                }
            }

            if(m_netProviders.Count > 0)
            {
                foreach(INetWorkProvider netWork in m_netProviders.Values)
                {
                    netWork.Recv();
                }
            }
        }

        public void StartPing()
        {
            WooLogger.Info("StartPing");
            m_pingTask = TimerManager.Instance.AddRepeatTask(5, 5, -1, () =>
            {
                SendNotification(PingName);
                return false;
            });
        }


        public void StopPing()
        {
            WooLogger.Info("StopPing");
            if (m_pingTask != null)
            {
                TimerManager.Instance.RemoveTask(m_pingTask);
                m_pingTask = null;
            }
        }
    }
}